#include "Arduino.h"
#include "stdbool.h"
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

static const uint8_t availableIOs[] =
    {
        16,
        17,
        18,
        8,
        3,
        9,
        10,
        11,
};

void checkIOAvail(lua_State *L, int pin)
{
    for (uint8_t i = 0; i < sizeof(availableIOs); ++i)
    {
        if (availableIOs[i] == pin)
        {
            return;
        }
    }
    luaL_error(L, "invalid pin");
}

static int lua_gpio_mode(lua_State *L)
{
    int pin = luaL_checkinteger(L, 1);
    int mode = luaL_checkinteger(L, 2);
    checkIOAvail(L, pin);
    pinMode(pin, mode);
    return 0;
}

static int lua_gpio_write(lua_State *L)
{
    int pin = luaL_checkinteger(L, 1);
    int value = luaL_checkinteger(L, 2);
    checkIOAvail(L, pin);
    digitalWrite(pin, value);
    return 0;
}

static int lua_gpio_read(lua_State *L)
{
    int pin = luaL_checkinteger(L, 1);
    checkIOAvail(L, pin);
    int value = digitalRead(pin);
    lua_pushinteger(L, value);
    return 1;
}

static const luaL_Reg gpioLib[] = {
    {"mode", lua_gpio_mode},
    {"read", lua_gpio_read},
    {"write", lua_gpio_write},
    {"INPUT", NULL},
    {"OUTPUT", NULL},
    {"INPUT_PULLUP", NULL},
    {"INPUT_PULLDOWN", NULL},
    {NULL, NULL},
};

LUAMOD_API int luaopen_gpio(lua_State *L)
{
    luaL_newlib(L, gpioLib);
    lua_pushinteger(L, INPUT);
    lua_setfield(L, -2, "INPUT");
    lua_pushinteger(L, OUTPUT);
    lua_setfield(L, -2, "OUTPUT");
    lua_pushinteger(L, INPUT_PULLUP);
    lua_setfield(L, -2, "INPUT_PULLUP");
    lua_pushinteger(L, INPUT_PULLDOWN);
    lua_setfield(L, -2, "INPUT_PULLDOWN");
    return 1;
}